package com.lightjet.macross.base.awf.service.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.ReadContext;

import com.lightjet.macross.base.awf.config.*;
import com.lightjet.macross.base.awf.domain.BizFormIndex;
import com.lightjet.macross.base.awf.domain.BizProcessConfig;
import com.lightjet.macross.base.awf.dto.*;
import com.lightjet.macross.base.awf.enums.AWFInfoEnum;
import com.lightjet.macross.base.awf.service.*;
import com.lightjet.macross.base.awf.util.DTOConvertUtil;
import com.lightjet.macross.common.dto.ResultDTO;
import com.lightjet.macross.common.util.FormatUtil;
import com.lightjet.macross.common.util.RedisUtil;
import com.mongodb.BasicDBObject;
import io.netty.util.internal.StringUtil;
import lombok.extern.java.Log;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.*;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.impl.persistence.entity.ExecutionEntityImpl;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.IdentityLink;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskInfo;
import org.activiti.image.ProcessDiagramGenerator;
import org.bson.types.ObjectId;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import javax.annotation.Resource;
import java.awt.*;
import java.io.InputStream;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.List;
import java.util.stream.Collectors;


import static com.lightjet.macross.base.awf.config.AWFConstants.*;
import static com.lightjet.macross.base.awf.enums.AWFInfoEnum.*;
import static java.util.stream.Collectors.toList;

/**
 * @author : lijia
 * @version : 1.0 2019-02-16 09:44
 * description : 流程服务层接口实现 封装activiti 流程服务实现
 */

@Service
@Log
public class ProcessServiceImpl implements ProcessService {
    @Resource
    private ProcessEngine processEngine;

    @Resource
    private RuntimeService runtimeService;

    @Resource
    private TaskService taskService;

    @Resource
    private IdentityService identityService;

    @Resource
    private MongoTemplate mongoTemplate;

    @Resource
    private BizFormIndexService bizFormIndexService;

    @Resource
    private RepositoryService repositoryService;

    @Resource
    private ManagementService managementService;

    @Resource
    private HistoryService historyService;

    @Resource
    private ProcessEngineConfiguration processEngineConfiguration;

    @Resource
    private BizFormLogService bizFormLogService;

    @Resource
    private BizAgentService bizAgentService;

    @Resource
    private BizProcessConfigService bizProcessConfigService;

    @Resource
    private RedisUtil redisUtil;
    /**
     * description:发起流程
     *
     * @param startFormDTO
     */
    @Override
    @Transactional
    public ResultDTO<String> StartProcess(FlowDataDTO startFormDTO) {
        String spiid="";
        try {
            if(startFormDTO.getExecutor()==null)
            {
                return ResultDTO.CreateErrorResult(PROCESSSTART_NOORIGINATOR_ERROR.toString());
            }
            identityService.setAuthenticatedUserId(startFormDTO.getExecutor()); //设置流程发起人
           /* String startfomrkey = UUID.randomUUID().toString();//生成一个ID并将流程一下节点人员 和 表单暂存放到 initdata 这个collection中
            HashMap<String,Object> init = new HashMap(){
                {put("nextexecutor",startFormDTO.getNextexecutor());}
                {put("bizformdata",startFormDTO.getBizformdata());}
            };
            mongoTemplate.insert(init,"initdata");//插入mongodb 供首个节点进行人员任务分配
            Map<String, Object> variables = new HashMap<String, Object>() {{
                put("startfomrkey", startfomrkey);
            }};*/
            Map<String, Object> variables = new HashMap<String, Object>() {{
                put(AWFConstants.ORIGINATOR_INNER_VARIABLE, startFormDTO.getExecutor());
            }};
            if(!StringUtil.isNullOrEmpty(startFormDTO.getNextrouteflag()))
            {
                variables.put(FLAG_VARIABLE,startFormDTO.getNextrouteflag());
            }
            ProcessInstance pi = runtimeService.startProcessInstanceByKey(startFormDTO.getProcesskey(),variables);////按照流程定义的key启动流程实例
            spiid = pi.getId();
            log.info(MessageFormat.format(AWFInfoEnum.PROCESSSTARTSUCCESS_INFO.toString(),pi.getProcessDefinitionName(), pi.getProcessDefinitionId(), pi.getId(), startFormDTO.getExecutor()));
            startFormDTO.setProcesskey(pi.getProcessDefinitionId());
            startFormDTO.setOpdate(new Date().getTime());
            startFormDTO.setProcinstId(pi.getId());
            //设置流程标题 如启动参数中未设置则使用默认标题格式
            if(StringUtil.isNullOrEmpty(startFormDTO.getTitle()))
            {
                startFormDTO.setTitle(MessageFormat.format(DEFAULT_PROCESS_TITLE_FORMATE,startFormDTO.getExecutor(), FormatUtil.GetDateTimeStr(startFormDTO.getOpdate()) ,pi.getId()));
            }
            runtimeService.setProcessInstanceName(pi.getId(),startFormDTO.getTitle());
            //自动完成第一个任务处理
            Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).taskAssignee(startFormDTO.getExecutor()).singleResult();//自动获取第一个节点任务
            if (task != null){//正常获取到任务后，把发起时的表单进行保存
                startFormDTO.setTaskId(task.getId());
                startFormDTO.setNodename(task.getName());
                startFormDTO.setActivityId(task.getTaskDefinitionKey());
                HashMap<String, Object> form;
                ObjectMapper mapper = new ObjectMapper();
                form = mapper.readValue(mapper.writeValueAsString(startFormDTO), HashMap.class);
                List<BizFormIndex> indexes = bizFormIndexService.GetFormIndexes(pi.getProcessDefinitionId());//获取流程索引集合
                ReadContext context = JsonPath.parse(startFormDTO.getBizformdata());//通过jsonpath 抽取索引并添加到 formdata 存放到mongo
                for(BizFormIndex idx : indexes)
                {
                    form.put(idx.getIndexname(), GetJsonVal(context, idx.getIndexpath(), idx.getPattern()));
                }
                mongoTemplate.insert(form,AWFConstants.FORMDATA_COLLECTIONNAME);
                //备份记录到数据库bizformlog表
                bizFormLogService.CreateBizFormLog(form);
                log.info(MessageFormat.format(FORMSAVESUCCESS_INFO.toString(), startFormDTO.getProcinstId(),startFormDTO.getTaskId()));
                taskService.complete(task.getId());
                log.info(MessageFormat.format(STARTNODEAUTOFINISHSUCCESS_INFO.toString(), startFormDTO.getProcinstId(),startFormDTO.getTaskId()));

            }
            else{
                throw new RuntimeException(STARTNODEAUTOFINISH_ERROR.toString());
            }
            //log.info(MessageFormat.format("表单保存成功，表单ID【{0}】", form.get("_id")));
            //新建一个该流程的状态缓存信息
/*            if(redisUtil!=null)
            {
                redisUtil.set("awfp-status::piid:"+pi.getProcessInstanceId(), GetProcInstStatus(pi.getProcessInstanceId()));
                log.info("建立了流程状态缓存信息："+"awfp-status::piid:"+task.getProcessInstanceId());
            }*/
            ResultDTO<String> res = ResultDTO.CreateOKResult(pi.getProcessInstanceId());
            res.setContent(res.getMessage());
            return res;
        }
        catch (Exception e)
        {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            if(!StringUtil.isNullOrEmpty(spiid)) {//流程发起失败后需要从mongodb库删除已插入的数据
                Query query = new Query();
                Criteria criteria = new Criteria();
                criteria.and("procinstId").is(spiid);
                query.addCriteria(criteria);
                mongoTemplate.remove(query, AWFConstants.FORMDATA_COLLECTIONNAME);//需要刪除mongodb的已插入表单信息
                log.info("已从MongoDB删除发起失败的流程:"+spiid);
            }
            log.info(PROCESSSTART_ERROR.toString()+e.getMessage());
            return ResultDTO.CreateErrorResult(PROCESSSTART_ERROR.toString()+e.getMessage());
        }

    }

    /**
     * description:流程节点创建时用于给流程dataform进行标记 明确当前节点需要读取的上一点，该节点以前的节点均设置为过期
     *
     * @param procinstid
     * @param activityid
     */
    @Override
    public void ProcessNodeForm(String procinstid, String activityid) {
        Query query=new Query();
        Criteria criteria = new Criteria();
        criteria.and("procinstId").is(procinstid).and("expired").is(0);
        query.addCriteria(criteria);
        //query.with(new Sort(new Sort.Order(Sort.Direction.ASC,"_id")));
        query.with(new Sort(Sort.Direction.ASC,"_id"));
        List<HashMap> list = mongoTemplate.find(query,HashMap.class,AWFConstants.FORMDATA_COLLECTIONNAME);
        Integer count = list.size();
        String prevNode ="";
        Long expdate = 0L;
        for(int i= count-1;i>=0;i--)//扫描最后一个非当前节点的上一节点
        {
            if(!list.get(i).get("activityId").toString().equals(activityid) && prevNode.equals("")) { //找到上一个节点名称
                prevNode = list.get(i).get("activityId").toString();
            }
            if(!prevNode.equals("") && !list.get(i).get("activityId").toString().equals(prevNode)) //找到最后一个不是上一个节点的节点
            {
                expdate = (Long) list.get(i).get("opdate");
                break;
            }
        }
        if(prevNode.equals(""))
        {
            throw new RuntimeException(MessageFormat.format(GETFORM_PREVIOUS_ERROR.toString(),procinstid,activityid));
        }
        else
        {
            //将上上个节点的expired 设置为1
            query=new Query();
            query.addCriteria(Criteria.where("procinstId").is(procinstid).and("opdate").lte(expdate));
            Update update = Update.update("expired", 1);
            mongoTemplate.updateMulti(query, update, AWFConstants.FORMDATA_COLLECTIONNAME);
        }

    }

    /**
     * description:获取上个节点的表单列表
     *
     * @param procinstid
     * @param activityid
     */
    @Override
    public List<HashMap> GetPreviousForms(String procinstid, String activityid) {
        Query query=new Query();
        Criteria criteria = new Criteria();
        criteria.and("procinstId").is(procinstid).and("expired").is(0).and("activityId").ne(activityid);
        query.addCriteria(criteria);
        //query.with(new Sort(new Sort.Order(Sort.Direction.DESC,"_id")));
        query.with(new Sort(Sort.Direction.DESC,"_id"));
        return mongoTemplate.find(query,HashMap.class,AWFConstants.FORMDATA_COLLECTIONNAME);
    }

    @Override
    public List<HashMap> GetPreviousUnExpiredForms(String procinstid, String activityid) {
        Query query=new Query();
        Criteria criteria = new Criteria();
        criteria.and("procinstId").is(procinstid).and("expired").is(0);
        query.addCriteria(criteria);
        //query.with(new Sort(new Sort.Order(Sort.Direction.DESC,"_id")));
        query.with(new Sort(Sort.Direction.DESC,"_id"));
        return mongoTemplate.find(query,HashMap.class,AWFConstants.FORMDATA_COLLECTIONNAME);
    }


    /**
     * description:根据taskid 获取上个节点的表单列表
     *
     * @param taskid
     */
    @Override
    public List<HashMap> GetPreviousFormsByTaskId(String taskid) {
        Task task = taskService.createTaskQuery().taskId(taskid).singleResult();
        if(task!=null)
        {
            return GetPreviousForms(task.getProcessInstanceId(),task.getTaskDefinitionKey());
        }
        else
        {
            throw new RuntimeException(MessageFormat.format(GETTASK_ERROR.toString(),taskid));
        }
    }

    /**
     * description:根据taskid 以及节信息（节点名称或节点formkey）获取
     *
     * @param taskid
     * @param node
     */
    @Override
    public List<HashMap> GetPreviousFormsByTaskIdAndNode(String taskid, String node) {
        Task task = taskService.createTaskQuery().taskId(taskid).singleResult();
        if(task!=null) {
            Query query = new Query();
/*            query.addCriteria(
                    new Criteria().andOperator(
                            Criteria.where("procinstId").is(task.getProcessInstanceId()),
                            new Criteria().orOperator(
                                    Criteria.where("nodename").is(11),
                                    Criteria.where("formkey").is(22)
                            )
                    )
            );*/
            Criteria criteria = new Criteria();
            criteria.and("procinstId").is(task.getProcessInstanceId()).and("nodename").is(node);
            query.addCriteria(criteria);
            //query.with(new Sort(new Sort.Order(Sort.Direction.DESC,"_id")));
            query.with(new Sort(Sort.Direction.DESC, "_id"));
            return mongoTemplate.find(query, HashMap.class, AWFConstants.FORMDATA_COLLECTIONNAME);
        }
        else
        {
            throw new RuntimeException(MessageFormat.format(GETTASK_ERROR.toString(),taskid));
        }
    }

    /**
     * description:完成节点处理
     *
     * @param formDTO
     */
    @Transactional
    @Override
    public ResultDTO<String> ExecuteProcess(FlowDataDTO formDTO) {
        Task task = taskService.createTaskQuery().taskId(formDTO.getTaskId()).singleResult();
        if (task != null) {
            //首先校验 任务所属的流程实例ID是否正在进行服务任务处理
            if(redisUtil!=null)
            {
                if(redisUtil.hasKey("awfp::piid_"+task.getProcessInstanceId()))
                {
                    return ResultDTO.CreateErrorResult(TASKEXECUTE_PROCESSING_ERROR);
                }
                //删除缓存键
                if(redisUtil.hasKey("awfp-status::piid:"+task.getProcessInstanceId())) {
                    redisUtil.del("awfp-status::piid:" + task.getProcessInstanceId());
                    log.info("删除了流程状态缓存信息：" + "awfp-status::piid:" + task.getProcessInstanceId());
                }
            }
            else
            {
                log.info("redisUtil为空，无法进行业务处理防重操作");
            }
            if (task.getAssignee() == null || task.getAssignee().equals(""))//有多人可参与非会签类型
            {
                List<IdentityLink> candidates =  taskService.getIdentityLinksForTask(formDTO.getTaskId());
                if(candidates.stream().filter(e->e.getUserId().equals(formDTO.getExecutor())).count()==0) //如果当前处理人不在参与列队，
                {
                    log.warning(MessageFormat.format(TASKEXECUTE_CANDIDATE_ERROR.toString(),formDTO.getTaskId(),formDTO.getExecutor()));
                    return ResultDTO.CreateErrorResult(MessageFormat.format(TASKEXECUTE_CANDIDATE_ERROR.toString(),formDTO.getTaskId(),formDTO.getExecutor()));
                }
                log.info("尝试将节点【" + task.getName() + "】任务分配给" + formDTO.getExecutor() );
                taskService.claim(task.getId(), formDTO.getExecutor());
                log.info("节点【" + task.getName() + "】任务被" + formDTO.getExecutor() + "领取");
            }
            else
            {
                if (!task.getAssignee().equals(formDTO.getExecutor()))
                {
                    log.warning(MessageFormat.format(TASKEXECUTE_ASSIGNEE_ERROR.toString(),formDTO.getTaskId(),formDTO.getExecutor()));
                    return ResultDTO.CreateErrorResult(MessageFormat.format(TASKEXECUTE_ASSIGNEE_ERROR.toString(),formDTO.getTaskId(),formDTO.getExecutor()));
                }
            }
            formDTO.setProcesskey(task.getProcessDefinitionId());
            formDTO.setProcinstId(task.getProcessInstanceId());
            formDTO.setOpdate(new Date().getTime());
            formDTO.setNodename(task.getName());
            formDTO.setActivityId(task.getTaskDefinitionKey());
            HashMap<String, Object> form;
            ObjectMapper mapper = new ObjectMapper();
            try {
                form = mapper.readValue(mapper.writeValueAsString(formDTO), HashMap.class);
            }
            catch (Exception e)
            {
                return ResultDTO.CreateErrorResult(MessageFormat.format(GETFORM_PARSE_ERROR.toString(),e.getMessage()));
            }
            List<BizFormIndex> indexes = bizFormIndexService.GetFormIndexes(task.getProcessDefinitionId());//获取流程索引集合
            ReadContext context = JsonPath.parse(formDTO.getBizformdata());//通过jsonpath 抽取索引并添加到 formdata 存放到mongo
            for(BizFormIndex idx : indexes)
            {
                form.put(idx.getIndexname(), GetJsonVal(context, idx.getIndexpath(), idx.getPattern()));
            }
            //自主生成ID 方便后面回滚时根据ID进行删除！！！
            form.put("_id",new ObjectId());
            mongoTemplate.insert(form,AWFConstants.FORMDATA_COLLECTIONNAME);
            //备份记录到数据库bizformlog表
            bizFormLogService.CreateBizFormLog(form);
            log.info(MessageFormat.format(FORMSAVESUCCESS_INFO.toString(), formDTO.getProcinstId(),formDTO.getTaskId()));

            //设置下一节点流程走向标志（如果存在网关）
            Map<String, Object> vars = new HashMap(); //参数
            if (formDTO.getNextrouteflag()!= null && !formDTO.getNextrouteflag().equals("")) {
                vars.put(FLAG_VARIABLE, formDTO.getNextrouteflag());
                if(formDTO.getNextrouteflag().equals(DEFAULT_DENYFLAG))
                {
                    vars.put(DENY_MULTISIGN_VARIABLE,1);
                }
            }

            try {
                taskService.complete(task.getId(), vars);
                log.info("节点【" + task.getName() + "】任务处理成功！");
            }
            catch (Exception e)
            {
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                if(form!=null && form.get("_id")!=null) {
                    Query query = new Query();
                    //Criteria criteria = new Criteria();
                    //criteria.and("procinstId").is(task.getProcessInstanceId()).and("expired").is(0).and("taskId").is(task.getId());
                    query.addCriteria(new Criteria().and("_id").is(form.get("_id")));
                    mongoTemplate.remove(query, AWFConstants.FORMDATA_COLLECTIONNAME);//需要刪除mongodb的已插入表单信息
                }
                Query query2=new Query();
                query2.addCriteria(Criteria.where("procinstId").is(task.getProcessInstanceId()).and("expired").is(1));
                Update update = Update.update("expired", 0);//更新表单标识
                mongoTemplate.updateMulti(query2, update, AWFConstants.FORMDATA_COLLECTIONNAME);

                log.info(MessageFormat.format(TASKEXECUTE_ERROR.toString(),task.getId()+":"+task.getName(),e.getMessage()));
                return ResultDTO.CreateErrorResult(MessageFormat.format(TASKEXECUTE_ERROR.toString(),task.getId()+":"+task.getName(),e.getMessage()));
            }
            return ResultDTO.CreateOKResult();
        }
        else
        {
            return ResultDTO.CreateErrorResult(MessageFormat.format(GETTASK_ERROR.toString(),formDTO.getTaskId()));
        }
    }

    @Override
    public List<Task> GetAllMyTask(String userId) {
        List<Task> tasks = taskService//与任务相关的Service
                .createTaskQuery()//创建一个任务查询对象
                .taskCandidateOrAssigned(userId)
                .list();
        return tasks;
    }

    @Override
    public List GetMyBizTask(String userId,String pdkey) {
        List<Task> tasks = taskService.createTaskQuery().taskCandidateOrAssigned(userId).processDefinitionKey(pdkey).list();
        List<BizFormIndex> indexes = bizFormIndexService.GetFormIndexes(pdkey);//获取流程索引集合
        List<Map<String, String>> retList = tasks.stream().map(e ->
                GetMappedTask(e,indexes,null,null)
        ).collect(toList());
        return retList;
    }

    /**
     * description:根据用户ID和查询条件获取我的业务任务（带索引信息）
     *
     * @param userId
     * @param pdkey
     * @param tdkey
     * @param bizquery
     */
    @Override
    public List<HashMap<String, String>> GetMyBizTaskByQuery(String userId, String pdkey, String tdkey, HashMap<String, String> bizquery) {
        List<Task> tasks;
        if(pdkey.split(":").length>1)
        {
            tasks = tdkey.equals("*")?taskService.createTaskQuery().
                    taskCandidateOrAssigned(userId).
                    processDefinitionId(pdkey).
                    list()
                    : taskService.createTaskQuery().
                    taskCandidateOrAssigned(userId).
                    processDefinitionId(pdkey).
                    taskDefinitionKey(tdkey).
                    list();
        }
        else
        {
            tasks = tdkey.equals("*")?taskService.createTaskQuery().
                    taskCandidateOrAssigned(userId).
                    processDefinitionKey(pdkey).
                    list()
                    : taskService.createTaskQuery().
                    taskCandidateOrAssigned(userId).
                    processDefinitionKey(pdkey).
                    taskDefinitionKey(tdkey).
                    list();
        }
        List<BizFormIndex> indexes = bizFormIndexService.GetFormIndexes(pdkey);//获取流程索引集合
        Map<String,String> formKeyMap = bizProcessConfigService.FindNodeConfigMap(pdkey, BizProcessConfig.ConfigTypes.HANDLERURL);
        List<HashMap<String, String>> retList = tasks.stream().map(e ->
                GetMappedTask(e,indexes,bizquery,formKeyMap)
        )//.filter(x->!x.get("_id").toString().equals("-"))
                .collect(toList());
        return retList;
    }




/*    @Override
    public Map<String, Long> GetMyBizTaskSummary(String userId) {
        List<Task> tasks = taskService//与任务相关的Service
                .createTaskQuery()//创建一个任务查询对象
                .taskCandidateOrAssigned(userId)
                .list();
        Map<String, Long> counting = tasks.stream().collect(
                Collectors.groupingBy(e -> e.getTaskDefinitionKey()+"|" + e.getTaskDefinitionKey(), Collectors.counting()));

        return counting;
    }*/
    /**
     * description:获取我的任务的汇总信息
     *
     * @param userId
     */
    @Override
    public List<ProcDefDTO> GetMyBizTaskSummary(String userId){
        List<Task> tasks = taskService//与任务相关的Service
                .createTaskQuery()//创建一个任务查询对象
                .taskCandidateOrAssigned(userId)
                .list();
        return GetProcDefDTOList(tasks);
    }

    @Override
    public List<ProcDefDTO> GetMyBizTaskSummary(String userId, List<String> keylist) {
        List<Task> tasks = taskService//与任务相关的Service
                .createTaskQuery()//创建一个任务查询对象
                .taskCandidateOrAssigned(userId)
                .processDefinitionKeyIn(keylist)
                .list();
        return GetProcDefDTOList(tasks);
    }

    @Override
    public List<ProcDefDTO> GetMyBizTaskSummary(String userId,String pdkey){
        List<Task> tasks = taskService//与任务相关的Service
                .createTaskQuery()//创建一个任务查询对象
                .taskCandidateOrAssigned(userId)
                .processDefinitionKey(pdkey)
                .list();
        return GetProcDefDTOList(tasks);
    }

    private List<ProcDefDTO> GetProcDefDTOList(List<Task> tasks)
    {
        List<ProcDefDTO> procList = new ArrayList<>();
        /*Map<String, Long> counting = tasks.stream().collect(
                Collectors.groupingBy(e -> e.getTaskDefinitionKey()+"|" + e.getTaskDefinitionKey(), Collectors.counting()));*/
        Map<String,Map<String, NodeDefDTO>> maps = new HashMap<>();
        for(Task task : tasks)
        {
            if(!maps.containsKey(task.getProcessDefinitionId()))
            {
                maps.put(task.getProcessDefinitionId(),new HashMap<String,NodeDefDTO>(){{put(task.getTaskDefinitionKey(),new NodeDefDTO(task.getTaskDefinitionKey(),task.getName(),1));}});
            }
            else
            {
                if(maps.get(task.getProcessDefinitionId()).containsKey(task.getTaskDefinitionKey()))
                {
                    maps.get(task.getProcessDefinitionId()).get(task.getTaskDefinitionKey()).setTaskcount(maps.get(task.getProcessDefinitionId()).get(task.getTaskDefinitionKey()).getTaskcount()+1);
                }
                else
                {
                    maps.get(task.getProcessDefinitionId()).put(task.getTaskDefinitionKey(),new NodeDefDTO(task.getTaskDefinitionKey(),task.getName(),1));
                }
            }
        }
        for(String k:maps.keySet())
        {
            ProcessDefinition p = processEngine.getRepositoryService()//与流程定义和部署对象相关的Service
                    .createProcessDefinitionQuery().processDefinitionId(k).singleResult();

            procList.add(new ProcDefDTO(p.getKey(),
                    p.getName()==null?p.getKey():p.getName(),
                    maps.get(k).values().stream().mapToInt(NodeDefDTO::getTaskcount).sum(),
                    p.getId(),
                    (p.getVersion()+""),
                    maps.get(k).values().stream().map(e->e).collect(toList()),null
            ));
        }
        return procList;

    }
    private HashMap<String, String> GetMappedTask(TaskInfo e, List<BizFormIndex> indexList, HashMap<String,String> bizquery, Map<String,String> formKeyMap) {
        //Object formid = e.getProcessVariables().get("_formid");
        String deleUrl = (formKeyMap!=null&&formKeyMap.containsKey(e.getTaskDefinitionKey()))?formKeyMap.get(e.getTaskDefinitionKey()):e.getFormKey();
        Object formid = taskService.getVariable(e.getId(),"_formid");
        Query query=new Query();
        Criteria criteria = new Criteria();
        if(formid==null)
        {
            log.info("未在变量中找到获取monggodb formid");
            criteria.and("taskId").is(bizFormLogService.FindLatestTaskIdByPiid( e.getProcessInstanceId()));
            //formid = new ObjectId(bizFormLogService.FindMonggoFormId(e.getId()));
        }
        else
        {
            criteria.and("_id").is(formid);
        }


        List<String> variables = new ArrayList<>();
        //query.addCriteria(Criteria.where("_id").is(formid));
        if(bizquery!=null)
        {
            if(bizquery.keySet().contains("variable")) //如果存在 查询流程变量的查询参数
            {
                variables = Arrays.asList(bizquery.get("variable").split("\\|"));
                bizquery.remove("variable");
            }
            for(String key: bizquery.keySet())
            {
                criteria.and(key).regex(".*?\\" +bizquery.get(key)+ ".*");
            }
        }
        query.addCriteria(criteria);
        List<HashMap> form = mongoTemplate.find(query,HashMap.class,AWFConstants.FORMDATA_COLLECTIONNAME);
        HashMap<String, String> ret = new HashMap<>();
        if(form.size()==1) {
            form.get(0).remove("bizformdata");
            form.get(0).put("_id",form.get(0).get("_id").toString());
            form.get(0).put("taskId", e.getId());
            form.get(0).put("nodename", e.getName());
            form.get(0).put("activityId", e.getTaskDefinitionKey());
            form.get(0).put("formkey", deleUrl);// e.getFormKey());
            form.get(0).put("formname", e.getFormKey());// e.getFormKey());
            form.get(0).put("opdate", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(e.getCreateTime()));
            ret = form.get(0);
        }
        else
        {
            HashMap<String, String> retMap = new HashMap<String, String>() {
                {put("taskId", e.getId());}
                {put("procinstId", e.getProcessInstanceId());}
                {put("nodename", e.getName());}
                {put("activityId", e.getTaskDefinitionKey());}
                {put("formkey",deleUrl);}// e.getFormKey());}
                {put("formname", e.getFormKey());}
                {put("opdate", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(e.getCreateTime()));}
                {put("_id","-");}
            };
            ret = retMap;

        }
        if(variables.size()>0)
        {
            for (String v : variables) {
                Object pv = taskService.getVariable(e.getId(),v);
                ret.put(v, pv==null?"":pv.toString());
            }
        }
        return ret;

    }

    /**
     * description:获取当前同级节点的表单列表（主要用于会签节点）
     *
     * @param procinstid
     * @param activityid
     */
    @Override
    public List<HashMap> GetCurrentForms(String procinstid, String activityid) {
        Query query=new Query();
        Criteria criteria = new Criteria();
        criteria.and("procinstId").is(procinstid).and("expired").is(0).and("activityId").is(activityid);
        query.addCriteria(criteria);
        //query.with(new Sort(new Sort.Order(Sort.Direction.DESC,"_id")));
        query.with(new Sort(Sort.Direction.DESC,"_id"));
        return mongoTemplate.find(query,HashMap.class,AWFConstants.FORMDATA_COLLECTIONNAME);
    }

    /**
     * description:根据任务号获取当前同级节点的表单列表（主要用于会签节点）
     *
     * @param taskid
     */
    @Override
    public List<HashMap> GetCurrentForms(String taskid) {
        Task task = taskService.createTaskQuery().taskId(taskid).singleResult();
        if(task!=null)
        {
            return GetCurrentForms(task.getProcessInstanceId(),task.getTaskDefinitionKey());
        }
        else
        {
            throw new RuntimeException(MessageFormat.format(GETTASK_ERROR.toString(),taskid));
        }
    }

    /**
     * description:获取当前处理表单最后一个节点的所有表单
     *
     * @param procinstid
     */
    @Override
    public List<HashMap> GetLastForms(String procinstid) {
        Query query=new Query();
        Criteria criteria = new Criteria();
        criteria.and("procinstId").is(procinstid).and("expired").is(0);
        query.addCriteria(criteria);
        //query.with(new Sort(new Sort.Order(Sort.Direction.DESC,"_id")));
        query.with(new Sort(Sort.Direction.DESC,"_id"));
        //有小问题 如果最后一个是会签节点的话 要处理下 因为会签会由多个form 有可能4个实例 只提交了2个！！！！！！！！！！！
        return mongoTemplate.find(query,HashMap.class,AWFConstants.FORMDATA_COLLECTIONNAME);
    }

    @Override
    @Transactional(rollbackFor=Exception.class)
    public ResultDTO<String> JumpTask(String currentTaskid, String targetTaskDefId,String jumpReason,String operid) {
        //当前任务
        Task currentTask = taskService.createTaskQuery().taskId(currentTaskid).singleResult();
        if(currentTask!=null) {
            if (currentTask.getTaskDefinitionKey().equals(targetTaskDefId)) {
                return ResultDTO.CreateErrorResult(AWFInfoEnum.JUMPTOSELF_ERROR);
            }
            //获取流程定义
            Process process = repositoryService.getBpmnModel(currentTask.getProcessDefinitionId()).getMainProcess();


            //删除当前所有任务
            List<Task> taskList = taskService.createTaskQuery().processInstanceId(currentTask.getProcessInstanceId()).list();
            Integer taskCount = taskList.size();
            for(int i= taskCount-1;i>=0;i--)
            {
                //删除当前任务前先网dataform表里插入上一节点的复制数据
                HashMap<String, Object> form = GetLastForms(currentTask.getProcessInstanceId()).get(0);

                //删除当前运行任务
                //Task siblingTask = taskService.createTaskQuery().executionId(e.getId()).singleResult();
                form.put("taskId", taskList.get(i).getId());
                form.remove("_id");
                form.put("nodename", taskList.get(i).getName());
                form.put("activityId", taskList.get(i).getTaskDefinitionKey());
                form.put("executor", StringUtil.isNullOrEmpty(operid)?"-":operid);//跳过节点的处理人员统一为 “-”
                form.put("comments",(jumpReason==null||"".equals(jumpReason))?"流程跳转":jumpReason);
                form.put("opdate", new Date().getTime());
                mongoTemplate.insert(form, AWFConstants.FORMDATA_COLLECTIONNAME);
                //备份记录到数据库bizformlog表
                bizFormLogService.CreateBizFormLog(form);

                log.info("删除流程taskId："+taskList.get(i) +";executionId:"+taskList.get(i).getExecutionId());
                String executionEntityId = managementService.executeCommand(new DeleteTaskCmd(taskList.get(i).getId()));
            }
            Execution execution = runtimeService.createExecutionQuery().executionId(currentTask.getExecutionId()).singleResult();
            Execution parentExecution = runtimeService.createExecutionQuery().executionId( execution.getParentId()).singleResult();
            if(((ExecutionEntityImpl) parentExecution).isMultiInstanceRoot())
            {
                log.info("当前任务为多实例任务");
                List<Execution> children =  runtimeService.createExecutionQuery().parentId(parentExecution.getId()).list();
                for(int i= children.size()-1;i>=0;i--)
                {
                    managementService.executeCommand(new DeleteExecutionCmd(children.get(i).getId()));
                    log.info(MessageFormat.format("删除流程多实例父Execution【{0}】的子例Execution【{1}】：",parentExecution.getId(),children.get(i).getId()));
                }
                execution = parentExecution;
                //多实例节点需要把 子execution及其变量删除， 因为主execution 会被更细掉 而子execution 不会被处理
            }

            //获取目标节点定义
            FlowNode targetNode = (FlowNode) process.getFlowElement(targetTaskDefId);

            log.info("执行流程跳转executionId:"+execution.getId());
            try {
                //流程执行到来源节点
                managementService.executeCommand(new JumpTaskCmd(targetNode, execution.getId()));

                return ResultDTO.CreateOKResult();
            }
            catch (Exception e)
            {
                //流程跳转失败
                //回滚mongodb
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                for(Task t: taskList)
                {
                    Query query = Query.query(Criteria.where("procinstId").is(t.getProcessInstanceId()).and("taskId").is(t.getId()));
                    mongoTemplate.remove(query,FORMDATA_COLLECTIONNAME);
                }
                log.info(JUMPFINISH_ERROR+",已回滚删除业务表数据:"+e.getMessage());
                return ResultDTO.CreateErrorResult(JUMPFINISH_ERROR+":"+e.getMessage());
            }
        }
        else
        {
            return ResultDTO.CreateErrorResult(MessageFormat.format(GETTASK_ERROR.toString(),currentTaskid));
        }
    }

    /**
     * description:获取流程查看图
     *
     * @param procinstid
     */
    @Override
    public InputStream GetProcessView(String procinstid) {
        //获取历史流程实例
        HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(procinstid).singleResult();
        //获取流程图
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
        processEngineConfiguration = processEngine.getProcessEngineConfiguration();
        // ProcessDiagramGenerator diagramGenerator = processEngineConfiguration.getProcessDiagramGenerator();
        ProcessDefinitionEntity definitionEntity = (ProcessDefinitionEntity)repositoryService.getProcessDefinition(processInstance.getProcessDefinitionId());
        List<HistoricActivityInstance> highLightedActivitList = historyService.createHistoricActivityInstanceQuery().processInstanceId(procinstid).list();
        List<String> highLightedActivitis = new ArrayList<String>();
        List<String> highLightedFlows = getHighLightedFlows(bpmnModel,definitionEntity,highLightedActivitList);
        for(HistoricActivityInstance tempActivity : highLightedActivitList) {
            String activityId = tempActivity.getActivityId();
            highLightedActivitis.add(activityId);
        }
        ICustomProcessDiagramGenerator diagramGenerator = (ICustomProcessDiagramGenerator) processEngineConfiguration
                .getProcessDiagramGenerator();
        Set<String> currIds = runtimeService.createExecutionQuery().processInstanceId(processInstance.getId()).list()
                .stream().map(Execution::getActivityId).collect(Collectors.toSet());
        return diagramGenerator.generateDiagram(bpmnModel, "png", highLightedActivitis,
                highLightedFlows, "宋体","微软雅黑","黑体", null, 2.0, new Color[] { AWFConstants.COLOR_NORMAL, AWFConstants.COLOR_CURRENT }, currIds);
        //return diagramGenerator.generateDiagram(bpmnModel, "png", highLightedActivitis, highLightedFlows,"宋体","微软雅黑","黑体",null,2.0);
    }

    /**
     * description:获取当前最新版本流程定义
     */
    @Override
    public List<ProcDefDTO> GetProcessLatestDefSet() {
        List<ProcessDefinition> list = processEngine.getRepositoryService()
                .createProcessDefinitionQuery()
                .orderByProcessDefinitionVersion().asc() // 使用流程定义的版本升序排列
                .list();
        Map<String, ProcessDefinition> map = new LinkedHashMap<String, ProcessDefinition>();
        if (list != null && list.size() > 0) {
            for (ProcessDefinition pd : list) {
                map.put(pd.getKey(), pd);
            }
        };
        return map.values().stream().map(e-> new ProcDefDTO(e.getKey(),e.getName(),0,e.getId(),e.getVersion()+"",null,null)).collect(Collectors.toList());
    }

    /**
     * description:获取当前所有流程定义
     */
    @Override
    public List<ProcDefDTO> GetProcessAllDefSet() {
        List<ProcessDefinition> list = processEngine.getRepositoryService()
                .createProcessDefinitionQuery()
                .orderByProcessDefinitionVersion().asc() // 使用流程定义的版本升序排列
                .list();

        return list.stream().map(e-> new ProcDefDTO(e.getKey(),e.getName(),0,e.getId(),e.getVersion()+"",null,null)).collect(Collectors.toList());
    }

    /**
     * description:获取当前所有在途流程
     */
    @Override
    public List<ProcDefDTO> GetRunningProcess(String pdkey) {
        List<ProcDefDTO> maplist = new ArrayList<>();
        List<ProcessInstance> list = (StringUtil.isNullOrEmpty(pdkey) || "*".equals(pdkey) )? runtimeService.createProcessInstanceQuery().list():
                runtimeService.createProcessInstanceQuery().processDefinitionKey(pdkey).list();
        for(ProcessInstance inst : list)
        {
            ProcDefDTO p = maplist.stream().filter(x->x.getProcdefid().equals(inst.getProcessDefinitionId())).findFirst().orElse(null);
            if(p!=null)
            {
                p.getInstlist().add(DTOConvertUtil.FromProcessInstance(inst));
            }
            else
            {
                p= new ProcDefDTO(
                        inst.getProcessDefinitionKey(),
                        inst.getProcessDefinitionName()==null?inst.getProcessDefinitionKey():inst.getProcessDefinitionName(),
                        1,
                        inst.getProcessDefinitionId(),
                        inst.getProcessDefinitionVersion().toString(),null,null);
                List<ProcInstDTO> l = new ArrayList<>();
                l.add(DTOConvertUtil.FromProcessInstance(inst));
                p.setInstlist(l);
                maplist.add(p);
            }
        }
        return maplist;
    }

    /**
     * description:获取当前所有在途流程的流程定义信息汇总 无明细【Stream 汇总 写法范例】
     */
    @Override
    public List<ProcDefDTO> GetRunningProcessSummary(String pdkey) {

        List<ProcessInstance> list = (StringUtil.isNullOrEmpty(pdkey) || "*".equals(pdkey) )? runtimeService.createProcessInstanceQuery().list():
                runtimeService.createProcessInstanceQuery().processDefinitionKey(pdkey).list();
        return getMyTaskSummaryByList(list);
//        Map<List<String>, Long> map =
//                list.stream()
//                        .collect(Collectors.groupingBy(
//                                f ->
//                                        Arrays.asList(f.getProcessDefinitionKey(),
//                                                f.getProcessDefinitionName()==null?f.getProcessDefinitionKey():f.getProcessDefinitionName(),
//                                                f.getProcessDefinitionId(), (f.getProcessDefinitionVersion()+"")),
//                                Collectors.counting()
//                        ));
//        List<ProcDefDTO> result =
//                map.entrySet()
//                        .stream()
//                        .map(e -> new ProcDefDTO(e.getKey().get(0),e.getKey().get(1),Integer.parseInt(e.getValue().toString()),e.getKey().get(2), e.getKey().get(3),null,null))
//                        .collect(Collectors.toList());
//
//        return result;
    }

    @Override
    public List<ProcDefDTO> GetRunningProcessSummary(Set<String> pdkeys) {
        List<ProcessInstance> list = runtimeService.createProcessInstanceQuery().processDefinitionKeys(pdkeys).list();
        return getMyTaskSummaryByList(list);
    }

    private List<ProcDefDTO> getMyTaskSummaryByList(List<ProcessInstance> list){
        Map<List<String>, Long> map =
                list.stream()
                        .collect(Collectors.groupingBy(
                                f ->
                                        Arrays.asList(f.getProcessDefinitionKey(),
                                                f.getProcessDefinitionName()==null?f.getProcessDefinitionKey():f.getProcessDefinitionName(),
                                                f.getProcessDefinitionId(), (f.getProcessDefinitionVersion()+"")),
                                Collectors.counting()
                        ));
        List<ProcDefDTO> result =
                map.entrySet()
                        .stream()
                        .map(e -> new ProcDefDTO(e.getKey().get(0),e.getKey().get(1),Integer.parseInt(e.getValue().toString()),e.getKey().get(2), e.getKey().get(3),null,null))
                        .collect(Collectors.toList());

        return result;
    }

    /**
     * description:根据流程定义ID获取当前在途流程的明细
     *
     * @param pdid
     */
    @Override
    public List<ProcInstDTO> GetRunningProcessDetail(String pdid) {
        return runtimeService.createProcessInstanceQuery().processDefinitionId(pdid).list().stream().map(e-> DTOConvertUtil.FromProcessInstance(e,GetCurrentTask(e.getProcessInstanceId()))).collect(toList());
    }

    /**
     * description:根据流程定义ID获取 节点定义信息
     *
     * @param pdid
     */
    @Override
    public List<NodeDefDTO> GetTaskNodeDefSet(String pdid) {
        BpmnModel model = repositoryService.getBpmnModel(pdid);
        List<NodeDefDTO> list = new ArrayList<>();
        if(model != null) {
            Collection<FlowElement> flowElements = model.getMainProcess().getFlowElements();
            for(FlowElement e : flowElements) {
                if(e.getClass().getName().equals("org.activiti.bpmn.model.UserTask"))
                {
                    list.add(new NodeDefDTO(e.getId(), e.getName(), 1));
                }
            }
        }
        return  list;
    }

    /**
     * description:根据流程定义ID获取 节点定义信息
     *
     * @param pdid
     */
    @Override
    public List<NodeDefDTO> GetTaskNodeDefSetWithServiceTask(String pdid) {
        BpmnModel model = repositoryService.getBpmnModel(pdid);
        List<NodeDefDTO> list = new ArrayList<>();
        if(model != null) {
            Collection<FlowElement> flowElements = model.getMainProcess().getFlowElements();
            for(FlowElement e : flowElements) {
                if(e.getClass().getName().equals("org.activiti.bpmn.model.UserTask")|| e.getClass().getName().equals("org.activiti.bpmn.model.ServiceTask"))
                {
                    list.add(new NodeDefDTO(e.getId(), e.getName(), 1));
                }
            }
        }
        return  list;
    }

    /**
     * description:根据流程实例ID 获取任务处理历史信息
     *
     * @param piid
     */
    @Override
    public List<TaskInstDTO> GetTaskHistory(String piid) {
        return historyService.createHistoricTaskInstanceQuery().
                processInstanceId(piid).list().stream().
                sorted(Comparator.comparing(HistoricTaskInstance::getStartTime)).
                map(x->new TaskInstDTO(x.getId(),x.getName(),x.getAssignee(),FormatUtil.GetDateTimeStr(x.getStartTime()),FormatUtil.GetDateTimeStr(x.getEndTime()))).
                collect(toList());
    }

    @Override
    public ResultDTO<String> DeleteProcess(List<String> piids,String reason) {
        for (String piid : piids) {
            try {
                //清空mongodb中的数据
                Query query = Query.query(Criteria.where("procinstId").is(piid));
                mongoTemplate.remove(query,FORMDATA_COLLECTIONNAME);
                log.info(MessageFormat.format("已删除Mongodb【{0}】中流程实例ID【{1}】的数据",FORMDATA_COLLECTIONNAME,piid));
                if(runtimeService.createProcessInstanceQuery().processInstanceId(piid).list().size() > 0)
                {
                    runtimeService.deleteProcessInstance(piid, StringUtil.isNullOrEmpty(reason) ? "流程人为删除" : reason);
                    log.info(MessageFormat.format("已删除Activiti中流程实例ID【{0}】的运行数据",piid));
                }
                if(historyService.createHistoricProcessInstanceQuery().processInstanceId(piid).list().size()>0)
                {
                    historyService.deleteHistoricProcessInstance(piid);//删除历史记录
                    log.info(MessageFormat.format("已删除Activiti中流程实例ID【{0}】的历史数据",piid));
                }

            }
            catch (Exception e)
            {
                return ResultDTO.CreateErrorResult("流程【"+piid+"】删除失败："+e.getMessage());
            }
        }

        return ResultDTO.CreateOKResult();
    }

    @Override
    public ResultDTO<String> FixTask(List<String> tiids) {
        StringBuilder stringBuilder = new StringBuilder();
        for(String tiid : tiids)
        {
            Task task = taskService.createTaskQuery().taskId(tiid).singleResult();
            if(task!=null) //要改成常量
            {
                if(task.getAssignee().equals(PROCESSFIX_ADMIN_ID.toString())) {
                    try {
                        //把flag变量置成 前序服务节点的名称
                        Map<String, Object> vars = new HashMap(); //参数

                        String prevsnname = runtimeService.getVariable(task.getExecutionId(),PREV_SERVICE_NODENAME)!=null?runtimeService.getVariable(task.getExecutionId(),PREV_SERVICE_NODENAME).toString():"流程结束处理";
                        vars.put(FLAG_VARIABLE, prevsnname);
                        taskService.complete(tiid,vars);//多个服务节点进入修复节点后 按原路返回
                        log.info(MessageFormat.format(TASKFIX_SUCCESS_INFO.toString(),tiid));
                    }
                    catch (Exception e)
                    {
                        log.info(MessageFormat.format(TASKFIX_BIZ_ERROR.toString(),tiid,e.getMessage()));
                        stringBuilder.append(MessageFormat.format(TASKFIX_BIZ_ERROR.toString(),tiid,e.getMessage()));
                    }

                }
                else
                {
                    stringBuilder.append(MessageFormat.format(TASKFIX_ASSINEENOTMATCH_ERROR.toString(),tiid,task.getAssignee(),PROCESSFIX_ADMIN_ID.toString()));
                }
            }
            else
            {
                stringBuilder.append(MessageFormat.format(GETTASK_ERROR.toString(),tiid));
            }
        }
        String errortotal = stringBuilder.toString();
        if(StringUtil.isNullOrEmpty(errortotal))
        {
            return ResultDTO.CreateOKResult();
        }
        else
        {
            return ResultDTO.CreateErrorResult(errortotal);
        }

    }

    /**
     * description:获取流程审批历史信息
     *
     * @param taskid
     */
    @Override
    public List<HashMap> GetProcessAuditHistory(String taskid) {
        Task task = taskService.createTaskQuery().taskId(taskid).singleResult();
        if(task!=null)
        {
            BasicDBObject dbObject = new BasicDBObject();
            dbObject.put("procinstId", task.getProcessInstanceId());
            //指定返回的字段
            BasicDBObject fieldsObject = new BasicDBObject();
            fieldsObject.put("taskId", true);
            fieldsObject.put("nodename", true);
            fieldsObject.put("opdate", true);
            fieldsObject.put("comments", true);
            fieldsObject.put("executor", true);
            Query query = new BasicQuery(dbObject.toJson(), fieldsObject.toJson());

            query.with(new Sort(Sort.Direction.ASC, "_id"));

            return mongoTemplate.find(query, HashMap.class, AWFConstants.FORMDATA_COLLECTIONNAME);
        }
        else
        {
            throw new RuntimeException(MessageFormat.format(GETTASK_ERROR.toString(),taskid));
        }
    }

    /**
     * description:根据流程实例ID获取流程审批历史信息
     *
     * @param piid
     */
    @Override
    public List<HashMap> GetProcessAuditHistoryByPIID(String piid) {
        BasicDBObject dbObject = new BasicDBObject();
        dbObject.put("procinstId", piid);
        //指定返回的字段
        BasicDBObject fieldsObject = new BasicDBObject();
        fieldsObject.put("taskId", true);
        fieldsObject.put("nodename", true);
        fieldsObject.put("opdate", true);
        fieldsObject.put("comments", true);
        fieldsObject.put("executor", true);
        Query query = new BasicQuery(dbObject.toJson(), fieldsObject.toJson());

        query.with(new Sort(Sort.Direction.ASC, "_id"));

        return mongoTemplate.find(query, HashMap.class, AWFConstants.FORMDATA_COLLECTIONNAME);
    }

    /**
     * description:流程转办
     *
     * @param taskid
     * @param targetuserid
     */
    @Override
    public ResultDTO<String> ChangeAssinee(String taskid, String targetuserid) {
        String msg = "";
        if(StringUtil.isNullOrEmpty(targetuserid))
        {
            msg = MessageFormat.format(TASKDELEGATE_TARGET_ERROR.toString(),taskid);
            log.info(msg);
            return ResultDTO.CreateErrorResult(msg);
        }
        Task task = taskService.createTaskQuery().taskId(taskid).singleResult();
        if(task!=null)
        {
            if(StringUtil.isNullOrEmpty(task.getAssignee())) //如果任务以候选人的方式分配
            {
                if(!GetCandidateUsers(taskid).contains(targetuserid)) {
                    taskService.addCandidateUser(taskid, targetuserid);
                    msg = MessageFormat.format(TASKDELEGATE_ADDCANDIDATE_INFO.toString(), taskid, targetuserid);
                }
                else
                {
                    msg =  MessageFormat.format(TASKDELEGATE_CANDIDATEEXIST_INFO.toString(), taskid, targetuserid);
                }
            }
            else
            {
                if(!(task.getAssignee().equals(targetuserid))) {

                    taskService.setAssignee(taskid, targetuserid);
                    msg = MessageFormat.format(TASKDELEGATE_CHANGEASSIGN_INFO.toString(),taskid,targetuserid);
                }
                else
                {
                    msg = MessageFormat.format(TASKDELEGATE_SAMEASSINEE_INFO.toString(),taskid);

                }
            }
            log.info(msg);
            return ResultDTO.CreateOKResult(msg);
        }
        else
        {
            msg = MessageFormat.format(GETTASK_ERROR.toString(),taskid);
            log.info(msg);
            throw new RuntimeException(msg);
        }
    }

    @Override
    public List<String> GetCandidateUsers(String taskid) {
        List<IdentityLink> identityLinkList = taskService.getIdentityLinksForTask(taskid);
        return identityLinkList.stream().map(IdentityLink::getUserId).collect(Collectors.toList());
    }



    /**
     * description:将制定流程定义的在途流程全部转给代理人 procdefkey 为* 表示全部
     *
     * @param procdefkey
     * @param agentid
     */
    @Override
    @Transactional
    public ResultDTO RunningProcAgent(String procdefkey, String principalid,String agentid) {
        StringBuilder sb = new StringBuilder();
        List<Task> taskList = procdefkey.equals("*")? taskService.createTaskQuery().taskCandidateOrAssigned(principalid).list()
                : taskService.createTaskQuery().processDefinitionKey(procdefkey).taskCandidateOrAssigned(principalid).list();
        for(Task task : taskList)
        {
            ResultDTO res = ChangeAssinee(task.getId(),agentid);
            if(!res.isSuccess())
            {
                sb.append(res.getMessage());
            }
        }
        if(sb.length()==0)
        {
            return ResultDTO.CreateOKResult();
        }
        else
        {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return ResultDTO.CreateErrorResult(sb.toString());
        }
    }

    /**
     * description:根据用户ID和起始时间查询范围内流程汇总信息
     *
     * @param userid
     * @param startdate
     * @param enddate
     */
    @Override
    public List<ProcDefDTO> GetHistorySummary(String userid, Date startdate, Date enddate) {
        Map<String, Long> map = historyService.createHistoricTaskInstanceQuery().
                taskCompletedBefore(enddate).
                taskCompletedAfter(startdate).
                taskAssignee(userid).
                list().stream().collect(Collectors.groupingBy(
                HistoricTaskInstance::getProcessDefinitionId,
                Collectors.counting()));
        return map.entrySet().stream()
                .map(e -> getProcDefByPIID(e.getKey(),e.getValue()))
                .collect(Collectors.toList());
    }

    @Override
    public List<ProcDefDTO> GetHistorySummary(String userid, String pdkey, Date startdate, Date enddate) {
        Map<String, Long> map = historyService.createHistoricTaskInstanceQuery().
                taskCompletedBefore(enddate).
                taskCompletedAfter(startdate).
                processDefinitionKey(pdkey).
                taskAssignee(userid).
                list().stream().collect(Collectors.groupingBy(
                HistoricTaskInstance::getProcessDefinitionId,
                Collectors.counting()));
        return map.entrySet().stream()
                .map(e -> getProcDefByPIID(e.getKey(),e.getValue()))
                .collect(Collectors.toList());
    }

    @Override
    public List<ProcDefDTO> GetHistorySummary(String userid, List<String> pdkeys, Date startdate, Date enddate) {
        Map<String, Long> map = historyService.createHistoricTaskInstanceQuery().
                taskCompletedBefore(enddate).
                taskCompletedAfter(startdate).
                processDefinitionKeyIn(pdkeys).
                taskAssignee(userid).
                list().stream().collect(Collectors.groupingBy(
                HistoricTaskInstance::getProcessDefinitionId,
                Collectors.counting()));
        return map.entrySet().stream()
                .map(e -> getProcDefByPIID(e.getKey(),e.getValue()))
                .collect(Collectors.toList());
    }

    /**
     * description:根据用户ID、流程定义ID以及查询条件获取已处理任务信息
     *
     * @param userId
     * @param pdid
     * @param bizquery
     */
    @Override
    public List<HashMap<String, String>> GetHistoryBizTaskByQuery(String userId, String pdid,Date startdate,Date enddate, HashMap<String, String> bizquery) {
        if(pdid.split(":").length>2) {
            List<HistoricTaskInstance> tasks = historyService.createHistoricTaskInstanceQuery().
                    taskAssignee(userId).
                    taskCompletedBefore(enddate).
                    taskCompletedAfter(startdate).
                    processDefinitionId(pdid).orderByTaskCreateTime().asc().
                    list();
            List<BizFormIndex> indexes = bizFormIndexService.GetFormIndexes(pdid);//获取流程索引集合
            return tasks.stream().map(e ->
                    GetMappedHisTask(e, indexes, bizquery)
            ).filter(x -> !x.get("_id").equals("-")).collect(toList());
        }
        else
        {
            List<HistoricTaskInstance> tasks = historyService.createHistoricTaskInstanceQuery().
                    taskAssignee(userId).
                    taskCompletedBefore(enddate).
                    taskCompletedAfter(startdate).
                    processDefinitionKey(pdid).orderByTaskCreateTime().asc().
                    list();
            List<BizFormIndex> indexes = bizFormIndexService.GetFormIndexes(pdid);//获取流程索引集合
            return tasks.stream().map(e ->
                    GetMappedHisTask(e, indexes, bizquery)
            ).filter(x -> !x.get("_id").equals("-")).collect(toList());
        }
    }

    @Override
    public List<HashMap<String, String>> GetHistoryBizTaskByQuery(String userId, String pdid, Date startdate, Date enddate, HashMap<String, String> bizquery, int pageindex, int pagesize) {
        if(pdid.split(":").length>2) {
            List<HistoricTaskInstance> tasks = historyService.createHistoricTaskInstanceQuery().
                    taskAssignee(userId).
                    taskCompletedBefore(enddate).
                    taskCompletedAfter(startdate).
                    processDefinitionId(pdid).orderByTaskCreateTime().asc().
                    listPage((pageindex-1)*pagesize,pagesize);
            List<BizFormIndex> indexes = bizFormIndexService.GetFormIndexes(pdid);//获取流程索引集合
            return tasks.stream().map(e ->
                    GetMappedHisTask(e, indexes, bizquery)
            ).filter(x -> !x.get("_id").equals("-")).collect(toList());
        }
        else
        {
            List<HistoricTaskInstance> tasks = historyService.createHistoricTaskInstanceQuery().
                    taskAssignee(userId).
                    taskCompletedBefore(enddate).
                    taskCompletedAfter(startdate).
                    processDefinitionKey(pdid).orderByTaskCreateTime().asc().
                    listPage((pageindex-1)*pagesize,pagesize);
            List<BizFormIndex> indexes = bizFormIndexService.GetFormIndexes(pdid);//获取流程索引集合
            return tasks.stream().map(e ->
                    GetMappedHisTask(e, indexes, bizquery)
            ).filter(x -> !x.get("_id").equals("-")).collect(toList());
        }
    }

    /**
     * description:根据mongodb formid 查找form
     *
     * @param formid
     */
    @Override
    public HashMap<String, Object> GetBizFormById(String formid) {
        Query query=new Query();
        Criteria criteria = new Criteria();
        criteria.and("_id").is(formid);
        query.addCriteria(criteria);
        List<HashMap> form = mongoTemplate.find(query,HashMap.class,AWFConstants.FORMDATA_COLLECTIONNAME);
        if(form.size()==1)
        {
            String taskId = form.get(0).get("taskId").toString();
            return form.get(0);
        }
        else
        {
            return null;
        }
    }

    /**
     * description:获取流程模型集合
     */
    @Override
    public List<ProcModelDTO> GetModelList(String modelname) {

        return repositoryService.createModelQuery().modelNameLike("%"+modelname+"%").list().stream().map(ProcModelDTO::new).collect(Collectors.toList());
    }

    /**
     * description:创建一个流程模型
     *
     * @param modelname
     * @param modelkey
     * @param description
     */
    @Override
    public ResultDTO<ProcModelDTO> CreateModel(String modelname, String modelkey, String description, String category) {
        try {
            ObjectMapper objectMapper = new ObjectMapper();
            ObjectNode editorNode = objectMapper.createObjectNode();
            editorNode.put("id", "canvas");
            editorNode.put("resourceId", "canvas");
            ObjectNode stencilSetNode = objectMapper.createObjectNode();
            stencilSetNode.put("namespace", "http://b3mn.org/stencilset/bpmn2.0#");
            editorNode.put("stencilset", stencilSetNode);
            Model modelData = repositoryService.newModel();

            ObjectNode modelObjectNode = objectMapper.createObjectNode();
            modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, modelname);
            modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);
            modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, description);
            modelData.setMetaInfo(modelObjectNode.toString());
            modelData.setName(modelname);
            modelData.setKey(modelkey);
            modelData.setCategory(category);
            //保存模型
            repositoryService.saveModel(modelData);
            repositoryService.addModelEditorSource(modelData.getId(), editorNode.toString().getBytes("utf-8"));
            return ResultDTO.CreateOKResult( ProcModelDTO.FromProcessInstance(modelData));
        }
        catch (Exception e)
        {
            return ResultDTO.CreateErrorResult("创建流程模型失败："+e.getMessage());
        }
    }

    /**
     * description:根据模型ID删除一个模型
     *
     * @param modelid
     */
    @Override
    public ResultDTO<String> DeleteModel(String modelid) {
        try {
            repositoryService.deleteModel(modelid);
            return ResultDTO.CreateOKResult();
        }
        catch (Exception e)
        {
            return ResultDTO.CreateErrorResult("删除模型出错："+e.getMessage());
        }
    }

    /**
     * description:从模型发布一个流程
     *
     * @param modelid
     */
    @Override
    public ResultDTO<String> DeployFromModel(String modelid) {
        try {
            Model modelData = repositoryService.getModel(modelid);
            ObjectNode modelNode = (ObjectNode) new ObjectMapper()
                    .readTree(repositoryService.getModelEditorSource(modelData.getId()));
            byte[] bpmnBytes = null;

            BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);
            bpmnBytes = new BpmnXMLConverter().convertToXML(model);

            String processName = modelData.getName() + ".bpmn20.xml";

            Deployment deployment = repositoryService.createDeployment().name("ModelDeployment-"+modelid).addString(processName, new String(bpmnBytes,"UTF-8"))
                    .deploy();

            return ResultDTO.CreateOKResult(deployment.getId());
        } catch (Exception e) {
            log.info(MessageFormat.format("根据模型部署流程失败：modelId={0}", modelid));
            return ResultDTO.CreateErrorResult(MessageFormat.format("根据模型部署流程失败：modelId={0}", modelid));
        }
    }

    /**
     * description:从现有模型拷贝一个新的模型
     *
     * @param modelid
     */
    @Override
    public ResultDTO CopyModel(String modelid,String modelname,String modelkey,String description,String category) {
        try {
            Model sourceModel = repositoryService.getModel(modelid);
            ObjectMapper objectMapper = new ObjectMapper();
            ObjectNode sourceObjectNode = (ObjectNode) new ObjectMapper().readTree(repositoryService.getModelEditorSource(modelid));
            Model model = repositoryService.newModel();
            ObjectNode modelObjectNode = objectMapper.createObjectNode();
            modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, modelname);
            modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);
            modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, description);

            model.setKey(modelkey);
            model.setName(modelname);
            model.setCategory(category);
            model.setMetaInfo(modelObjectNode.toString());

            repositoryService.saveModel(model);

            ObjectNode editorNode = sourceObjectNode.deepCopy();
            ObjectNode properties = new ObjectMapper().createObjectNode();
            properties.put("process_id", "");
            properties.put("process_author", "");
            properties.put("name", "");
            editorNode.set("properties", properties);
            repositoryService.addModelEditorSource(model.getId(), editorNode.toString().getBytes("utf-8"));

            return ResultDTO.CreateOKResult(ProcModelDTO.FromProcessInstance(model));
        }
        catch (Exception e)
        {
            return ResultDTO.CreateErrorResult(MessageFormat.format("复制模型【{0}】失败：{1}",modelid,e.getMessage()));
        }
    }

    /**
     * description:根据单条索引数据刷新该索引涉及历史表单
     *
     * @param formIndex
     */
    @Override
    @Transactional
    public ResultDTO<String> RefreshHisForm(BizFormIndex formIndex) {
        if(formIndex.getId()!=null && StringUtil.isNullOrEmpty(formIndex.getIndexpath()))
        {
            formIndex = bizFormIndexService.GetOne(formIndex.getId());
        }
        //查询到所有涉及到该索引的历史表单
        BasicDBObject dbObject = new BasicDBObject();
        BasicDBObject fieldsObject = new BasicDBObject();
        fieldsObject.put("_id", true);
        fieldsObject.put("processkey", true);
        fieldsObject.put("bizformdata", true);
        Query query = new BasicQuery(dbObject.toJson(), fieldsObject.toJson());
        query.addCriteria(new Criteria().and("processkey").regex(formIndex.getProcinstdefId()+ ".*"));
        List<HashMap> list = mongoTemplate.find(query,HashMap.class, AWFConstants.FORMDATA_COLLECTIONNAME);
        for(HashMap map : list)
        {
            //更新历史表单
            ReadContext context = JsonPath.parse(map.get("bizformdata").toString());
            String newindexval = GetJsonVal(context, formIndex.getIndexpath(), formIndex.getPattern());
            Update update = Update.update(formIndex.getIndexname(),newindexval);
            mongoTemplate.updateMulti(new Query().addCriteria(Criteria.where("_id").is(map.get("_id"))), update, AWFConstants.FORMDATA_COLLECTIONNAME);
        }
        return ResultDTO.CreateOKResult();
    }

    /**
     * description:根据任务ID获取流程定义ID
     *
     * @param taskid
     */
    @Override
    public String GetProcDefIdByTaskId(String taskid) {
        //Task task = taskService.createTaskQuery().taskId(taskid).singleResult();
        HistoricTaskInstance task = historyService.createHistoricTaskInstanceQuery().taskId(taskid).singleResult();
        if(task==null)
        {
            log.info(MessageFormat.format(GETTASK_ERROR.toString(),taskid));
            return "";
        }
        else
        {
            return task.getProcessDefinitionId();
        }
    }

    /**
     * description:根据流程定义ID获取流程描述 （用于选择人员Bean）
     *
     * @param piid
     */
    @Override
    public String GetProcDesByPIID(String piid) {
        String dfid=GetProcDefIdByPIID(piid);
        return dfid.equals("")?"-":repositoryService.getProcessDefinition(dfid).getDescription();

    }

    /**
     * description:根据流程定义ID获取流程描述 （用于选择人员Bean）
     *
     * @param pdid
     */
    @Override
    public String GetProcDesByPDID(String pdid) {
        ProcessDefinition p = repositoryService.getProcessDefinition(pdid);
        return p==null?"":p.getDescription();
    }

    /**
     * description:根据流程实例ID获取流程定义ID
     *
     * @param piid
     */
    @Override
    public String GetProcDefIdByPIID(String piid) {
        HistoricProcessInstance p = historyService.createHistoricProcessInstanceQuery().processInstanceId(piid).singleResult();
        return  p==null?"": p.getProcessDefinitionId();
    }

    /**
     * description:根据流程任务ID获取流程实例ID
     *
     * @param tid
     */
    @Override
    public String GetProcInstanceIdByTid(String tid) {
        Task task = taskService.createTaskQuery().taskId(tid).singleResult();
        return  task==null?"": task.getProcessInstanceId();
    }

    @Override
    public String GetTaskIdProcInstInfo(String piid, String nodename, String userid) {
        List<Task> taskList = taskService.createTaskQuery().taskCandidateOrAssigned(userid).processInstanceId(piid).taskName(nodename).list();
        if(taskList.size()>0)
        {
            return taskList.get(0).getId();
        }
        else
        {
            return "";
        }
    }

    /**
     * description:根据流程定义获取最新版本的流程定义实例
     *
     * @param pdkey
     */
    @Override
    public ProcDefDTO GetCurrentProcDef(String pdkey) {
        List<ProcessDefinition> defs = processEngine.getRepositoryService().createProcessDefinitionQuery().processDefinitionKey(pdkey)
                .orderByProcessDefinitionVersion().desc().list();
        if(defs.size()>0)
        {
            ProcessDefinition one = defs.get(0);
            return new ProcDefDTO(one.getKey(), one.getName(),0,one.getId(),one.getVersion()+"",null,null);
        }
        else
        {
            return null;
        }
    }

    /**
     * description:获取所有当前最新版本的流程定义集合
     */
    @Override
    public Set<String> GetAllLatestProcDef() {
        List<ProcessDefinition> definitions = repositoryService.createProcessDefinitionQuery().orderByProcessDefinitionVersion().asc().list();
        HashMap<String,String> definitionsMap = new HashMap<>();
        for(ProcessDefinition d : definitions)
        {
            definitionsMap.put(d.getKey(),d.getId());
        }
        return new HashSet<String>(definitionsMap.values());
    }

    @Override
    public List<ProcDefDTO> GetRunningInvolvedProcess(String userid, String pdkey) {
        List<HistoricProcessInstance> list = historyService.createHistoricProcessInstanceQuery().processDefinitionKey(pdkey).unfinished().involvedUser(userid).list();
//        Map<List<String>,Long> group = list.stream().collect(Collectors.groupingBy(f->
//                        Arrays.asList(StringUtil.isNullOrEmpty(f.getProcessDefinitionName())?f.getProcessDefinitionKey() :f.getProcessDefinitionName() ,f.getProcessDefinitionId(),f.getProcessDefinitionVersion().toString()),
//                Collectors.counting()));
//        return group.entrySet().stream()
//                .map(e -> new ProcDefDTO(e.getKey().get(0),e.getKey().get(0)+" (v"+e.getKey().get(2)+")",Integer.parseInt(e.getValue().toString()),e.getKey().get(1),"",null,null))
//                .collect(Collectors.toList());
        return GetInvolvedSummary(list);
    }

    @Override
    public List<ProcDefDTO> GetRunningInvolvedProcess(String userid, List<String> pdkeys) {
        List<HistoricProcessInstance> list = historyService.createHistoricProcessInstanceQuery().processDefinitionKeyIn(pdkeys).unfinished().involvedUser(userid).list();
        return GetInvolvedSummary(list);
    }

    private List<ProcDefDTO> GetInvolvedSummary(List<HistoricProcessInstance> list)
    {
        Map<List<String>,Long> group = list.stream().collect(Collectors.groupingBy(f->
                        Arrays.asList(StringUtil.isNullOrEmpty(f.getProcessDefinitionName())?f.getProcessDefinitionKey() :f.getProcessDefinitionName() ,f.getProcessDefinitionId(),f.getProcessDefinitionVersion().toString()),
                Collectors.counting()));
        return group.entrySet().stream()
                .map(e -> new ProcDefDTO(e.getKey().get(0),e.getKey().get(0)+" (v"+e.getKey().get(2)+")",Integer.parseInt(e.getValue().toString()),e.getKey().get(1),"",null,null))
                .collect(Collectors.toList());
    }

    @Override
    public List<NodeDefDTO> GetAllProcNodeDef(boolean withServiceNode) {
        Set<String> defs = GetAllLatestProcDef();
        List<NodeDefDTO> list = new ArrayList<>();
        for(String s : defs)
        {
            list.addAll(withServiceNode?GetTaskNodeDefSetWithServiceTask(s):GetTaskNodeDefSet(s));

        }
        return list;
    }


    @Override
    public List<String> GetProcessAssignees(String piid) {
        List<Task> taskList = taskService.createTaskQuery().processInstanceId(piid).orderByTaskCreateTime().desc().list();
        if(taskList!=null && taskList.size()>0)
        {
            if(StringUtil.isNullOrEmpty(taskList.get(0).getAssignee()))
            {
                List<IdentityLink> list = taskService.getIdentityLinksForTask(taskList.get(0).getId());
                return list.stream().map(IdentityLink::getUserId).collect(toList());

            }
            else
            {
                return Collections.singletonList(taskList.get(0).getAssignee());
            }
        }
        else
        {
            return null;
        }
    }

    /**
     * description:根据流程实例id获取流程定义信息
     *
     * @param piid
     */
    @Override
    public ProcDefDTO GetProcDefInfoByPIID(String piid) {
        HistoricProcessInstance hpi = historyService.createHistoricProcessInstanceQuery().processInstanceId(piid).singleResult();
        if(hpi!=null)
        {
            ProcDefDTO pdd = new ProcDefDTO();
            pdd.setProcdefid(hpi.getProcessDefinitionId());
            pdd.setProcdefkey(hpi.getProcessDefinitionKey());
            pdd.setProcdefname(hpi.getProcessDefinitionName());
            pdd.setVersion(hpi.getProcessDefinitionVersion().toString());
            return pdd;
        }
        else
        {
            return null;
        }
    }

    /**
     * description:获取用户在途涉及的流程汇总信息
     *
     * @param userid
     */
    @Override
    public List<ProcDefDTO> GetRunningInvolvedProcess(String userid) {
        List<HistoricProcessInstance> list = historyService.createHistoricProcessInstanceQuery().unfinished().involvedUser(userid).list();
        Map<List<String>, Long> group =
                list.stream()
                        .collect(Collectors.groupingBy(
                                f ->
                                        Arrays.asList(StringUtil.isNullOrEmpty(f.getProcessDefinitionName())? f.getProcessDefinitionKey() :f.getProcessDefinitionName(),
                                                f.getProcessDefinitionId(),
                                                f.getProcessDefinitionVersion().toString()),
                                Collectors.counting()
                        ));
        return group.entrySet().stream().
                map(e-> new ProcDefDTO(e.getKey().get(0),e.getKey().get(0)+"(v"+e.getKey().get(2)+")",
                        Integer.parseInt(e.getValue().toString()),e.getKey().get(1),"",null,null))
                .collect(Collectors.toList());
    }

    @Override
    public List<HashMap<String, Object>> GetRunningInvolvedProcessList(String pdid,String userid) {
        List<HistoricProcessInstance> list = historyService.createHistoricProcessInstanceQuery().unfinished().processDefinitionId(pdid).involvedUser(userid).list();
        List<BizFormIndex> indexes = bizFormIndexService.GetFormIndexes(pdid);//获取流程索引集合
        List<HashMap<String, Object>> retList = new ArrayList<>();

        for(HistoricProcessInstance his :list)
        {
            //获取当前节点信息
            // Task current = GetCurrentTask(his.getId());
            List<Task> currents = GetCurrentTasks(his.getId());
            //获取索引部分信息
            HashMap<String, Object> ret = GetMappedCurrentTask(his.getId(),indexes,null);
            ret.put("procinstId",his.getId()); //流程实例ID
            // ret.put("currentnode",current.getName()); //当前节点名称
            ret.put("currentnode", currents.size()>0 ? currents.get(0).getName() : "未知"); //当前节点名称
            List<String> candidates = new ArrayList<>();
            for(Task current : currents){
                if(StringUtil.isNullOrEmpty(current.getAssignee())) //如果任务以候选人的方式分配
                {
                    candidates.addAll(GetCandidateUsers(current.getId()));
                }
                else
                {
                    candidates.add(current.getAssignee());
                }
            }
            ret.put("candidates",candidates);
            retList.add(ret);
        }
        return  retList;
    }

    /**
     * description:根据任务ID获取业务表单
     *
     * @param taskid
     */
    @Override
    public String GetFormDataByTaskId(String taskid) {
        BasicDBObject dbObject = new BasicDBObject();
        BasicDBObject fieldsObject = new BasicDBObject();
        fieldsObject.put("bizformdata", true);
        Query query = new BasicQuery(dbObject.toJson(), fieldsObject.toJson());
        query.addCriteria(new Criteria().and("taskId").is(taskid));
        List<HashMap> list = mongoTemplate.find(query,HashMap.class, AWFConstants.FORMDATA_COLLECTIONNAME);
        if(list.size()==1)
        {
            return list.get(0).get("bizformdata").toString();
        }
        else
        {
            return "";
        }
    }

    @Override
    public ResultDTO<Object> GetBizFormDataByTaskId(String taskid) {
        BasicDBObject dbObject = new BasicDBObject();
        BasicDBObject fieldsObject = new BasicDBObject();
        fieldsObject.put("bizformdata", true);
        Query query = new BasicQuery(dbObject.toJson(), fieldsObject.toJson());
        query.addCriteria(new Criteria().and("taskId").is(taskid));
        List<HashMap> list = mongoTemplate.find(query,HashMap.class, AWFConstants.FORMDATA_COLLECTIONNAME);
        if(list.size()==1)
        {
            return ResultDTO.CreateOKResult(list.get(0).get("bizformdata"));
        }
        else
        {
            return ResultDTO.CreateErrorResult("未找到任务【"+taskid+"】的mongodb表单信息");
        }
    }

    /**
     * description:更新表单数据
     *
     * @param taskid
     * @param formdata
     */
    @Override
    public ResultDTO<String> UpdateFormData(String taskid, String formdata) {
        try {
            Query query = new Query();
            query.addCriteria(Criteria.where("taskId").is(taskid));
            Update update = new Update();
            update.set("bizformdata", formdata);//更新表单序列化文本
            //更新索引
            String pdid = GetProcDefIdByTaskId(taskid);
            List<BizFormIndex> indexes = bizFormIndexService.GetFormIndexes(pdid);//获取流程索引集合
            ReadContext context = JsonPath.parse(formdata);//通过jsonpath 抽取索引并添加到 formdata 存放到mongo
            for(BizFormIndex idx : indexes)
            {
                update.set(idx.getIndexname(), GetJsonVal(context, idx.getIndexpath(), idx.getPattern()));//根据最新
            }
            mongoTemplate.updateFirst(query, update, AWFConstants.FORMDATA_COLLECTIONNAME);
            return ResultDTO.CreateOKResult();
        }
        catch (Exception e)
        {
            log.info(MessageFormat.format(FORMDATA_UPDATE_ERROR.toString(),taskid,e.getMessage()));
            return ResultDTO.CreateErrorResult(MessageFormat.format(FORMDATA_UPDATE_ERROR.toString(),taskid,e.getMessage()));
        }

    }

    @Override
    public List<TaskInstDTO> GetTasksByPDKeyAndNodeName(String pdkey, String nodename) {
        if(pdkey.split(":").length>2) { //
            return taskService.createTaskQuery().processDefinitionId(pdkey).taskName(nodename).list().stream().
                    sorted(Comparator.comparing(Task::getCreateTime)).
                    map(x->new TaskInstDTO(x.getId(),x.getName(),x.getAssignee(),FormatUtil.GetDateTimeStr(x.getCreateTime()),FormatUtil.GetDateTimeStr(x.getCreateTime().getTime()+x.getDueDate().getTime()))).
                    collect(toList());

        }
        else
        {
            return taskService.createTaskQuery().processDefinitionKey(pdkey).taskName(nodename).list().stream().
                    sorted(Comparator.comparing(Task::getCreateTime)).
                    map(x->new TaskInstDTO(x.getId(),x.getName(),x.getAssignee(),FormatUtil.GetDateTimeStr(x.getCreateTime()),FormatUtil.GetDateTimeStr(x.getCreateTime().getTime()+x.getDueDate().getTime()))).
                    collect(toList());
        }
    }

    @Override
    public List<HashMap<String, String>> GetMyProcNodeFormList(String pdkey, String nodename, String userid) {
        List<Task> tasks = taskService.createTaskQuery()
                .taskCandidateOrAssigned(userid)
                .processDefinitionKey(pdkey)
                .taskName(nodename)
                .list();
        List<BizFormIndex> indexes = bizFormIndexService.GetFormIndexes(pdkey);//获取流程索引集合
        List<HashMap<String, String>> retList = tasks.stream().map(e ->
                GetMappedTask(e,indexes,null,null)
        ).collect(toList());
        return retList;
    }

    @Override
    @Cacheable(value = "awfp-status", key = "'piid:'+#piid",unless = "#result eq null")
    public Map<String, Set<String>> GetProcInstStatus(String piid) {
        Map<String,Set<String>> retMap = new HashMap<>();
        List<Task> taskList = taskService.createTaskQuery().processInstanceId(piid).list();
        if(taskList!=null && taskList.size()>0)
        {
            for(Task task: taskList)
            {
                retMap.put(task.getName(),new HashSet<>());
                if(StringUtil.isNullOrEmpty(task.getAssignee())) //
                {
                    List<IdentityLink> list = taskService.getIdentityLinksForTask(task.getId());
                    retMap.get(task.getName()).addAll(list.stream().map(IdentityLink::getUserId).collect(toList()));
                }
                else
                {
                    retMap.get(task.getName()).addAll(Collections.singletonList(taskList.get(0).getAssignee()));
                }
            }

        }
        else
        {
            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                    .processInstanceId(piid).orderByProcessInstanceEndTime().desc().list().stream().findFirst().orElse(null);
            if(historicProcessInstance!=null)
            {
                HashSet<String> userSet = new HashSet<>();
                userSet.add(FormatUtil.GetDateStr(historicProcessInstance.getEndTime(),"yyyy-MM-dd"));
                retMap.put("流程结束",userSet);
            }
            else
            {
                return null;
            }
        }
        return retMap;
    }

    @Override
    public HashMap GetTaskInfoByProps(String taskid, List<String> colums) {
        BasicDBObject dbObject = new BasicDBObject();
        dbObject.put("taskId", taskid);
        //指定返回的字段
        BasicDBObject fieldsObject = new BasicDBObject();
        for(String col:colums)
        {
            fieldsObject.put(col, true);
        }
        Query query = new BasicQuery(dbObject.toJson(), fieldsObject.toJson());
        query.with(new Sort(Sort.Direction.ASC, "_id"));
        List<HashMap> list = mongoTemplate.find(query, HashMap.class, AWFConstants.FORMDATA_COLLECTIONNAME);
        if(list.size()>0){
            return list.get(0);
        }
        else
        {
            return null;
        }
        //return mongoTemplate.find(query, HashMap.class, AWFConstants.FORMDATA_COLLECTIONNAME);
    }

    @Override
    public List<TaskStaticDTO> GetTaskStatic(String pdkey,boolean finished)
    {
        List<HistoricTaskInstance> taskInstanceList = new ArrayList<>();
        if(finished)
        {
            taskInstanceList = historyService.createHistoricTaskInstanceQuery().processDefinitionKey(pdkey).list();
        }
        else
        {
            taskInstanceList = historyService.createHistoricTaskInstanceQuery().unfinished().processDefinitionKey(pdkey).list();
        }
        Map<String,DoubleSummaryStatistics> maps = taskInstanceList.stream().collect(
                Collectors.groupingBy(
                        TaskInfo::getName, Collectors.summarizingDouble(f->(new Date().getTime() -f.getCreateTime().getTime()))));
        return maps.keySet().stream().map(e->
                new TaskStaticDTO(e, maps.get(e).getCount(),maps.get(e).getAverage(),maps.get(e).getSum())).collect(Collectors.toList());
/*        Map<String,TaskStaticDTO> map = taskInstanceList.stream().collect(
                Collectors.groupingBy(
                        TaskInfo::getName, new TaskStaticDTO("",1,1,1)));*/
    }

    @Override
    public Map<String, List<TaskInstDTO>> GetMappedTaskHisByPIIDs(List<String> piids) {
        if(piids!=null && piids.size()>0) {
            return historyService.createHistoricTaskInstanceQuery().processInstanceIdIn(piids).orderByHistoricTaskInstanceStartTime().desc().list().stream()
                    .collect(Collectors.groupingBy(
                            HistoricTaskInstance::getProcessInstanceId, Collectors.mapping(x ->
                                    new TaskInstDTO(x.getId(), x.getName(), x.getAssignee(), FormatUtil.GetDateTimeStr(x.getCreateTime()), FormatUtil.GetDateTimeStr(x.getEndTime())), Collectors.toList())));
        }
        else
        {
            return new HashMap<String, List<TaskInstDTO>>();
        }
    }

    private HashMap<String,Object> GetMappedCurrentTask(String piid,List<BizFormIndex> indexList, HashMap<String,String> bizquery)
    {
        HashMap<String,Object> ret = new HashMap<String,Object>();

        BasicDBObject dbObject = new BasicDBObject();
        dbObject.put("procinstId", piid);
        //指定返回的字段
        BasicDBObject fieldsObject = new BasicDBObject();
        for(BizFormIndex bfi : indexList)
        {
            fieldsObject.put(bfi.getIndexname(),true);
            ret.put(bfi.getIndexname(),"");
        }
        Query query = new BasicQuery(dbObject.toJson(), fieldsObject.toJson());
        query.with(new Sort(Sort.Direction.DESC, "_id"));
        List<HashMap> form = mongoTemplate.find(query,HashMap.class,AWFConstants.FORMDATA_COLLECTIONNAME);
        if(form.size()>0)
        {
            form.get(0).put("originator",form.get(form.size()-1).get("executor"));
            return form.get(0);
        }
        else
        {
            ret.put("originator","");
            return ret;
        }
    }


    private HashMap<String, String> GetMappedHisTask(TaskInfo e, List<BizFormIndex> indexList, HashMap<String,String> bizquery) {

        Query query=new Query();
        Criteria criteria = new Criteria();
        criteria.and("procinstId").is(e.getProcessInstanceId()).and("taskId").is(e.getId());

        if(bizquery!=null)
        {
            for(String key: bizquery.keySet())
            {
                criteria.and(key).regex(".*?\\" +bizquery.get(key)+ ".*");
            }
        }
        query.addCriteria(criteria);
        List<HashMap> form = mongoTemplate.find(query,HashMap.class,AWFConstants.FORMDATA_COLLECTIONNAME);

        HashMap<String, String> ret = new HashMap<>();
        if(form.size()==1) {
            form.get(0).remove("bizformdata");
            form.get(0).put("_id",form.get(0).get("_id").toString());
            form.get(0).put("taskId", e.getId());
            form.get(0).put("nodename", e.getName());
            form.get(0).put("activityId", e.getTaskDefinitionKey());
            form.get(0).put("formkey", e.getFormKey());
            form.get(0).put("opdate", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(e.getCreateTime()));
            ret = form.get(0);
        }
        else
        {
            HashMap<String, String> retMap = new HashMap<String, String>() {
                {put("taskId", e.getId());}
                {put("procinstId", e.getProcessInstanceId());}
                {put("nodename", e.getName());}
                {put("activityId", e.getTaskDefinitionKey());}
                {put("formkey", e.getFormKey());}
                {put("opdate", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(e.getCreateTime()));}
                {put("_id","-");}
            };
            ret = retMap;

        }

        return ret;

    }

    private ProcDefDTO getProcDefByPIID(String piid,Long count)
    {
        ProcessDefinition processDefinition = repositoryService.getProcessDefinition(piid);
        return new ProcDefDTO(processDefinition.getKey(),
                StringUtil.isNullOrEmpty(processDefinition.getName())?processDefinition.getKey():processDefinition.getName(),
                Integer.parseInt(count.toString()),
                piid,
                processDefinition.getVersion()+"",
                null,null
        );
    }

    private Task GetCurrentTask(String piid)
    {
        return taskService.createTaskQuery().processInstanceId(piid).orderByTaskCreateTime().desc().list().stream().findFirst().orElse(null);
    }

    private List<Task> GetCurrentTasks(String piid)
    {
        return taskService.createTaskQuery().processInstanceId(piid).orderByTaskCreateTime().desc().list();
    }

    private List<String> getHighLightedFlows(BpmnModel bpmnModel,ProcessDefinitionEntity processDefinitionEntity,List<HistoricActivityInstance> historicActivityInstances)
    {
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //24小时制
        List<String> highFlows = new ArrayList<String>();// 用以保存高亮的线flowId

        for (int i = 0; i < historicActivityInstances.size() - 1; i++)
        {
            // 对历史流程节点进行遍历
            // 得到节点定义的详细信息
            FlowNode activityImpl = (FlowNode)bpmnModel.getMainProcess().getFlowElement(historicActivityInstances.get(i).getActivityId());


            List<FlowNode> sameStartTimeNodes = new ArrayList<FlowNode>();// 用以保存后续开始时间相同的节点
            FlowNode sameActivityImpl1 = null;

            HistoricActivityInstance activityImpl_ = historicActivityInstances.get(i);// 第一个节点
            HistoricActivityInstance activityImp2_ ;

            for(int k = i + 1 ; k <= historicActivityInstances.size() - 1; k++)
            {
                activityImp2_ = historicActivityInstances.get(k);// 后续第1个节点

                if ( activityImpl_.getActivityType().equals("userTask") && activityImp2_.getActivityType().equals("userTask") &&
                        df.format(activityImpl_.getStartTime()).equals(df.format(activityImp2_.getStartTime())) ) //都是usertask，且主节点与后续节点的开始时间相同，说明不是真实的后继节点
                {

                }
                else
                {
                    sameActivityImpl1 = (FlowNode)bpmnModel.getMainProcess().getFlowElement(historicActivityInstances.get(k).getActivityId());//找到紧跟在后面的一个节点
                    break;
                }

            }
            sameStartTimeNodes.add(sameActivityImpl1); // 将后面第一个节点放在时间相同节点的集合里
            for (int j = i + 1; j < historicActivityInstances.size() - 1; j++)
            {
                HistoricActivityInstance activityImpl1 = historicActivityInstances.get(j);// 后续第一个节点
                HistoricActivityInstance activityImpl2 = historicActivityInstances.get(j + 1);// 后续第二个节点

                if (df.format(activityImpl1.getStartTime()).equals(df.format(activityImpl2.getStartTime())) )
                {// 如果第一个节点和第二个节点开始时间相同保存
                    FlowNode sameActivityImpl2 = (FlowNode)bpmnModel.getMainProcess().getFlowElement(activityImpl2.getActivityId());
                    sameStartTimeNodes.add(sameActivityImpl2);
                }
                else
                {// 有不相同跳出循环
                    break;
                }
            }
            List<SequenceFlow> pvmTransitions = activityImpl.getOutgoingFlows() ; // 取出节点的所有出去的线

            for (SequenceFlow pvmTransition : pvmTransitions)
            {// 对所有的线进行遍历
                FlowNode pvmActivityImpl = (FlowNode)bpmnModel.getMainProcess().getFlowElement( pvmTransition.getTargetRef());// 如果取出的线的目标节点存在时间相同的节点里，保存该线的id，进行高亮显示
                if (sameStartTimeNodes.contains(pvmActivityImpl)) {
                    highFlows.add(pvmTransition.getId());
                }
            }
        }
        return highFlows;
    }

    private String GetJsonVal(ReadContext context, String path, String pattern) {
        try {
            Object temp = context.read(path);
            if (pattern != null && !pattern.equals("")) {
                String[] p = pattern.split("\\|");
                if (p[0].equals("map")) {
                    Map<String, String> m = new ObjectMapper().readValue(p[1], Map.class);
                    if (m.containsKey(temp)) {
                        return m.get(temp);
                    }
                }
            }
            return temp.toString();
        } catch (Exception ex) {
            return "";
        }
    }

/*    public List<TaskInstDTO> GetHisTaskByPIIDs(List<String> piids)
    {
        List<HistoricTaskInstance> taskInstances = historyService.createHistoricTaskInstanceQuery().processInstanceIdIn(piids).list();
    }*/

}
